home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / imapd / imapd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-14  |  37.4 KB  |  1,260 lines

  1. /*
  2.  * Program:    IMAP2bis server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 November 1990
  13.  * Last Edited:    15 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Parameter files */
  37.  
  38. #include "mail.h"
  39. #include "osdep.h"
  40. #include <stdio.h>
  41. #include <ctype.h>
  42. #include <netdb.h>
  43. #include <errno.h>
  44. #include <signal.h>
  45. #include <pwd.h>
  46. #include <sys/file.h>
  47. #include <sys/stat.h>
  48. #include <sys/time.h>
  49. #include "misc.h"
  50.  
  51.  
  52. /* Autologout timer */
  53. #define TIMEOUT 60*30
  54.  
  55. /* Size of temporary buffers */
  56. #define TMPLEN 8192
  57.  
  58.  
  59. /* Server states */
  60.  
  61. #define LOGIN 0
  62. #define SELECT 1
  63. #define OPEN 2
  64. #define LOGOUT 3
  65.  
  66. /* Global storage */
  67.  
  68. char *version = "7.3(63)";    /* version number of this server */
  69. struct itimerval timer;        /* timeout state */
  70. int state = LOGIN;        /* server state */
  71. int mackludge = 0;        /* MacMS kludge */
  72. int anonymous = 0;        /* non-zero if anonymous */
  73. long idletime = 0;        /* time we became idle */
  74. MAILSTREAM *stream = NIL;    /* mailbox stream */
  75. MAILSTREAM *tstream = NIL;    /* temporary mailbox stream */
  76. long nmsgs = 0;            /* number of messages */
  77. long recent = 0;        /* number of recent messages */
  78. char *host = NIL;        /* local host name */
  79. char *user = NIL;        /* user name */
  80. char *pass = NIL;        /* password */
  81. char *home = NIL;        /* home directory */
  82. char *flags = NIL;        /* flag text */
  83. char cmdbuf[TMPLEN];        /* command buffer */
  84. char *tag;            /* tag portion of command */
  85. char *cmd;            /* command portion of command */
  86. char *arg;            /* pointer to current argument of command */
  87. char *lsterr = NIL;        /* last error message from c-client */
  88. char *response = NIL;        /* command response */
  89. char *litbuf = NIL;        /* buffer to hold literals */
  90.  
  91.  
  92. /* Response texts */
  93.  
  94. char *win = "%s OK %s completed\015\012";
  95. char *altwin = "%s OK %s\015\012";
  96. char *lose = "%s NO %s failed: %s\015\012";
  97. char *misarg = "%s BAD Missing required argument to %s\015\012";
  98. char *badfnd = "%s BAD FIND option unrecognized: %s\015\012";
  99. char *badarg = "%s BAD Argument given to %s when none expected\015\012";
  100. char *badseq = "%s BAD Bogus sequence in %s\015\012";
  101. char *badatt = "%s BAD Bogus attribute list in %s\015\012";
  102. char *badlit = "%s BAD Bogus literal count in %s\015\012";
  103. char *toobig = "* BAD Command line too long\015\012";
  104. char *nulcmd = "* BAD Null command\015\012";
  105. char *argrdy = "+ Ready for argument\015\012";
  106.  
  107. /* Drivers we use */
  108.  
  109. extern DRIVER bezerkdriver,tenexdriver,imapdriver,newsdriver,nntpdriver,
  110.   dummydriver;
  111.  
  112.  
  113. /* Function prototypes */
  114.  
  115. void main (int argc,char *argv[]);
  116. void clkint ();
  117. void kodint ();
  118. char *snarf (char **arg);
  119. void fetch (char *s,char *t);
  120. void fetch_body (long i,char *s);
  121. void fetch_body_part (long i,char *s);
  122. void fetch_envelope (long i,char *s);
  123. void fetch_encoding (long i,char *s);
  124. void changed_flags (long i,int f);
  125. void fetch_flags (long i,char *s);
  126. void fetch_internaldate (long i,char *s);
  127. void fetch_rfc822 (long i,char *s);
  128. void fetch_rfc822_header (long i,char *s);
  129. void fetch_rfc822_size (long i,char *s);
  130. void fetch_rfc822_text (long i,char *s);
  131. void penv (ENVELOPE *env);
  132. void pbody (BODY *body);
  133. void pstring (char *s);
  134. void paddr (ADDRESS *a);
  135. long cstring (char *s);
  136. long caddr (ADDRESS *a);
  137.  
  138. extern char *crypt (char *key,char *salt);
  139.  
  140. /* Main program */
  141.  
  142. void main (int argc,char *argv[])
  143. {
  144.   int i;
  145.   char *s,*t = "OK",*u,*v;
  146.   struct hostent *hst;
  147.   void (*f) () = NIL;
  148.   mail_link (&tenexdriver);    /* install the Tenex mail driver */
  149.   mail_link (&bezerkdriver);    /* install the Berkeley mail driver */
  150.   mail_link (&imapdriver);    /* install the IMAP driver */
  151.   mail_link (&newsdriver);    /* install the netnews driver */
  152.   mail_link (&nntpdriver);    /* install the NNTP driver */
  153.   mail_link (&dummydriver);    /* install the dummy driver */
  154.   gethostname (cmdbuf,TMPLEN-1);/* get local name */
  155.   host = cpystr ((hst = gethostbyname (cmdbuf)) ? hst->h_name : cmdbuf);
  156.   rfc822_date (cmdbuf);        /* get date/time now */
  157.   if (i = getuid ()) {        /* logged in? */
  158.     if (!(s = (char *) getlogin ())) s = (getpwuid (i))->pw_name;
  159.     if (i < 0) anonymous = T;    /* must be anonymous if pseudo-user */
  160.     user = cpystr (s);        /* set up user name */
  161.     pass = cpystr ("*");    /* and fake password */
  162.     state = SELECT;        /* enter select state */
  163.     t = "PREAUTH";        /* pre-authorized */
  164.   }
  165.   printf ("* %s %s IMAP2bis Service %s at %s\015\012",t,host,version,cmdbuf);
  166.   fflush (stdout);        /* dump output buffer */
  167.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  168.   signal (SIGUSR2,kodint);    /* prepare for Kiss Of Death */
  169.                 /* initialize timeout interval */
  170.   timer.it_interval.tv_sec = TIMEOUT;
  171.   timer.it_interval.tv_usec = 0;
  172.   do {                /* command processing loop */
  173.     idletime = time (0);    /* get the idle time now */
  174.                 /* get a command under timeout */
  175.     timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  176.     setitimer (ITIMER_REAL,&timer,NIL);
  177.     if (!fgets (cmdbuf,TMPLEN-1,stdin)) _exit (1);
  178.                 /* make sure timeout disabled */
  179.     timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  180.     setitimer (ITIMER_REAL,&timer,NIL);
  181.     idletime = 0;        /* not idle any more */
  182.                 /* no more last error or literal */
  183.     if (lsterr) fs_give ((void **) &lsterr);
  184.     if (litbuf) fs_give ((void **) &litbuf);
  185.                 /* find end of line */
  186.     if (!strchr (cmdbuf,'\012')) fputs (toobig,stdout);
  187.     else if (!(tag = strtok (cmdbuf," \015\012"))) fputs (nulcmd,stdout);
  188.     else if (!(cmd = strtok (NIL," \015\012")))
  189.       printf ("%s BAD Missing command\015\012",tag);
  190.     else {            /* parse command */
  191.       response = win;        /* set default response */
  192.       ucase (cmd);        /* canonicalize command case */
  193.                 /* snarf argument */
  194.       arg = strtok (NIL,"\015\012");
  195.                 /* LOGOUT command always valid */
  196.       if (!strcmp (cmd,"LOGOUT")) {
  197.     if (state == OPEN) mail_close (stream);
  198.     stream = NIL;
  199.     printf("* BYE %s IMAP2bis server terminating connection\015\012",host);
  200.     state = LOGOUT;
  201.       }
  202.  
  203.                 /* kludge for MacMS */
  204.       else if (!strcmp (cmd,"VERSION")) {
  205.                 /* single argument */
  206.     if (!(s = snarf (&arg))) response = misarg;
  207.     else if (arg) response = badarg;
  208.     else {
  209.       if (!((i = atoi (s)) && i > 0 && i <= 4))
  210.         response = "%s BAD Unknown version\015\012";
  211.       else if (mackludge = (i == 4))
  212.         fputs ("* OK [MacMS] The MacMS kludge is enabled\015\012",stdout);
  213.     }
  214.       }
  215.       else if (!strcmp (cmd,"NOOP")) {
  216.     if ((state == OPEN) && !mail_ping (stream)) {
  217.       printf ("* BYE %s Fatal mailbox error: %s\015\012",host,
  218.           lsterr ? lsterr : "<unknown>");
  219.       state = LOGOUT;    /* go away */
  220.     }
  221.       }
  222.       else switch (state) {    /* dispatch depending upon state */
  223.       case LOGIN:        /* waiting to get logged in */
  224.     if (!strcmp (cmd,"LOGIN")) {
  225.       struct stat sbuf;
  226.       struct passwd *pwd;
  227.       fs_give ((void **) &user);
  228.       fs_give ((void **) &pass);
  229.                 /* two arguments */
  230.       if (!((user = cpystr (snarf (&arg))) &&
  231.         (pass = cpystr (snarf (&arg))))) response = misarg;
  232.       else if (arg) response = badarg;
  233.                 /* see if username and password are OK */
  234.       else if (server_login (user,pass,&home,argc,argv)) state = SELECT;
  235.                 /* nope, see if we allow anonymous */
  236.       else if (!stat ("/etc/anonymous.newsgroups",&sbuf) &&
  237.            !strcmp (user,"anonymous") && (pwd = getpwnam ("nobody"))) {
  238.         anonymous = T;    /* note we are anonymous, login as nobody */
  239.         setgid (pwd->pw_gid);
  240.         setuid (pwd->pw_uid);
  241.         state = SELECT;    /* make selected */
  242.       }
  243.       else response = "%s NO Bad %s user name and/or password\015\012";
  244.     }
  245.     else response = "%s BAD Command unrecognized/login please: %s\015\012";
  246.     break;
  247.  
  248.       case OPEN:        /* valid only when mailbox open */
  249.                 /* fetch mailbox attributes */
  250.     if (!strcmp (cmd,"FETCH")) {
  251.       if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
  252.         response = misarg;
  253.       else fetch (s,t);    /* do the fetch */
  254.     }
  255.                 /* fetch partial mailbox attributes */
  256.     else if (!strcmp (cmd,"PARTIAL")) {
  257.       unsigned long msgno,start,count,size;
  258.       if (!(arg && (msgno = strtol (arg,&s,10)) && (t = strtok (s," ")) &&
  259.         (s = strtok (NIL,"\015\012")) && (start = strtol (s,&s,10)) &&
  260.         (count = strtol (s,&s,10)))) response = misarg;
  261.       else if (s && *s) response = badarg;
  262.       else if (msgno > stream->nmsgs) response = badseq;
  263.       else {        /* looks good */
  264.         u = s = NIL;    /* no strings yet */
  265.         if (!strcmp (ucase (t),"RFC822")) {
  266.                 /* have to make a temporary buffer for this */
  267.           size = mail_elt (stream,msgno)->rfc822_size;
  268.           s = u = (char *) fs_get (size + 1);
  269.           strcpy (u,mail_fetchheader (stream,msgno));
  270.           strcat (u,mail_fetchtext (stream,msgno));
  271.           u[size] = '\0';    /* tie off string */
  272.         }
  273.         else if (!strcmp (t,"RFC822.HEADER"))
  274.           size = strlen (s = mail_fetchheader (stream,msgno));
  275.         else if (!strcmp (t,"RFC822.TEXT"))
  276.           size = strlen (s = mail_fetchtext (stream,msgno));
  277.         else if (*t == 'B' && t[1] == 'O' && t[2] == 'D' && t[3] == 'Y' &&
  278.              t[4] == '[' && *(t += 5)) {
  279.           if ((v = strchr (t,']')) && !v[1]) {
  280.         *v = '\0';    /* tie off body part */
  281.         s = mail_fetchbody (stream,msgno,t,&size);
  282.           }
  283.         }
  284.         if (s) {        /* got a string back? */
  285.           if (size <= --start) s = NIL;
  286.           else {        /* have string we can make smaller */
  287.         s += start;    /* this is the first byte */
  288.                 /* tie off as appropriate */
  289.         if (count < (size - start)) s[count] = '\0';
  290.           }
  291.           printf ("* %ld FETCH %s (",msgno,t);
  292.           pstring (s);    /* write the string */
  293.           fputs (")\015\012",stdout);
  294.           if (u) fs_give ((void **) &u);
  295.         }
  296.         else response = badatt;
  297.       }
  298.     }
  299.  
  300.                 /* store mailbox attributes */
  301.     else if (!strcmp (cmd,"STORE")) {
  302.                 /* must have three arguments */
  303.       if (!(arg && (s = strtok (arg," ")) && (cmd = strtok (NIL," ")) &&
  304.         (t = strtok (NIL,"\015\012")))) response = misarg;
  305.       else if (!strcmp (ucase (cmd),"+FLAGS")) f = mail_setflag;
  306.       else if (!strcmp (cmd,"-FLAGS")) f = mail_clearflag;
  307.       else if (!strcmp (cmd,"FLAGS")) {
  308.         mail_clearflag (stream,s,flags);
  309.         f = mail_setflag;    /* gross, but rarely if ever done */
  310.       }
  311.       else response = "%s BAD STORE %s not yet implemented\015\012";
  312.       if (f) {        /* if a function was selected */
  313.         (*f) (stream,s,t);    /* do it */
  314.         fetch (s,"FLAGS");    /* get the new flags status */
  315.       }
  316.     }
  317.                 /* check for new mail */
  318.     else if (!strcmp (cmd,"CHECK")) {
  319.                 /* no arguments */
  320.       if (arg) response = badarg;
  321.       else if (anonymous) mail_ping (stream);
  322.       else mail_check (stream);
  323.     }
  324.                 /* expunge deleted messages */
  325.     else if (!(anonymous || strcmp (cmd,"EXPUNGE"))) {
  326.                 /* no arguments */
  327.       if (arg) response = badarg;
  328.       else mail_expunge (stream);
  329.     }
  330.                 /* copy message(s) */
  331.     else if (!(anonymous || strcmp (cmd,"COPY"))) {
  332.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  333.       else if (arg) response = badarg;
  334.       else mail_copy (stream,s,t);
  335.     }
  336.  
  337.                 /* search mailbox */
  338.     else if (!strcmp (cmd,"SEARCH")) {
  339.                 /* one or more arguments required */
  340.       if (!arg) response = misarg;
  341.       else {        /* zap search vector */
  342.                 /* end with a literal argument? */
  343.         while ((*(t = arg + strlen (arg) - 1) == '}') &&
  344.            (s = strrchr (arg,'{')) && response == win) {
  345.                 /* get length of literal, validate */
  346.           if (((i = atoi (++s)) < 1) || (TMPLEN - (t++ - cmdbuf) < i + 10))
  347.         response = badlit;
  348.           else {
  349.         *t++ = '\015';    /* reappend CR/LF */
  350.         *t++ = '\012';
  351.         fputs (argrdy,stdout);
  352.         fflush (stdout);/* dump output buffer */
  353.                 /* copy the literal */
  354.         while (i--) *t++ = getchar ();
  355.                 /* start a timeout */
  356.         timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  357.         setitimer (ITIMER_REAL,&timer,NIL);
  358.                 /* get new command tail */
  359.         if (!fgets (t,TMPLEN - (t-cmdbuf) - 1,stdin)) _exit (1);
  360.                 /* clear timeout */
  361.         timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  362.         setitimer (ITIMER_REAL,&timer,NIL);
  363.                 /* find end of line */
  364.         if (!(s = strchr (t,'\012'))) response = toobig;
  365.         else {
  366.           *s = NIL;    /* tie off line */
  367.           if (*--s == '\015') *s = NIL;
  368.         }
  369.           }
  370.         }
  371.                 /* punt if error */
  372.         if (response != win) break;
  373.                 /* do the search */
  374.         mail_search (stream,arg);
  375.         if (response == win || response == altwin) {
  376.                 /* output search results */
  377.           fputs ("* SEARCH",stdout);
  378.           for (i = 1; i <= nmsgs; ++i)
  379.         if (mail_elt (stream,i)->searched) printf (" %d",i);
  380.           fputs ("\015\012",stdout);
  381.         }
  382.       }
  383.     }
  384.     else            /* fall into select case */
  385.  
  386.       case SELECT:        /* valid whenever logged in */
  387.                 /* select new mailbox */
  388.     if ((!(anonymous || strcmp (cmd,"SELECT"))) ||
  389.         (!strcmp (cmd,"BBOARD")) || (!strcmp (cmd,"EXAMINE"))) {
  390.                 /* single argument */
  391.       if (!(s = snarf (&arg))) response = misarg;
  392.       else if (arg) response = badarg;
  393.       else {
  394.         char tmp[MAILTMPLEN];
  395.         sprintf (tmp,"%s%s",(*cmd == 'B') ? "*" : "",s);
  396.         recent = -1;    /* make sure we get an update */
  397.         if ((stream = mail_open (stream,tmp,anonymous ? OP_ANONYMOUS : NIL
  398.                      + (*cmd == 'E') ? OP_READONLY : NIL))
  399.         && ((response == win) || (response == altwin))) {
  400.                 /* flush old list */
  401.           fs_give ((void **) &flags);
  402.           s = tmp;        /* write flags here */
  403.           *s = '(';        /* start new flag list */
  404.           s[1] = '\0';
  405.           for (i = 0; i < NUSERFLAGS; i++)
  406.         if (t = stream->user_flags[i]) strcat (strcat (s,t)," ");
  407.                 /* append system flags to list */
  408.           strcat (s,"\\Answered \\Flagged \\Deleted \\Seen)");
  409.                 /* output list of flags */
  410.           printf ("* FLAGS %s\015\012",(flags = cpystr (s)));
  411.           state = OPEN;
  412.                 /* note readonly/readwrite */
  413.           response = stream->readonly ?
  414.         "%s OK [READ-ONLY] %s completed\015\012" :
  415.           "%s OK [READ-WRITE] %s completed\015\012";
  416.         }
  417.         else {        /* nuke if still open */
  418.           if (stream) mail_close (stream);
  419.           stream = NIL;
  420.           state = SELECT;    /* no mailbox open now */
  421.           response = lose;    /* open failed */
  422.         }
  423.       }
  424.     }
  425.                 /* APPEND message to mailbox */
  426.     else if (!(anonymous || strcmp (cmd,"APPEND"))) {
  427.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  428.       else if (arg) response = badarg;
  429.       else {        /* append the data */
  430.         STRING st;
  431.         INIT (&st,mail_string,(void *) t,strlen (t));
  432.         mail_append (NIL,s,&st);
  433.       }
  434.     }
  435.  
  436.                 /* find mailboxes or bboards */
  437.     else if (!strcmp (cmd,"FIND")) {
  438.       response = "%s OK FIND %s completed\015\012";
  439.                 /* get subcommand and true argument */
  440.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  441.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  442.         response = misarg;    /* missing required argument */
  443.       else if (arg) response = badarg;
  444.       else if (anonymous) {    /* special version for anonymous users */
  445.         if (!strcmp (cmd,"BBOARDS")) mail_find_bboards (NIL,s);
  446.         else if (!strcmp (cmd,"ALL.BBOARDS")) mail_find_all_bboard (NIL,s);
  447.                 /* bboards not supported here */
  448.             else response = badfnd;
  449.       }
  450.       else {        /* dispatch based on type */
  451.         if ((!strcmp (cmd,"MAILBOXES"))||(!strcmp (cmd,"ALL.MAILBOXES"))) {
  452.           if (*s == '{') tstream = mail_open (NIL,s,OP_HALFOPEN);
  453.           if (*cmd == 'A') { /* want them all? */
  454.         mail_find_all (NIL,s);
  455.         if (tstream) mail_find_all (tstream,s);
  456.           }
  457.           else {        /* just subscribed */
  458.         mail_find (NIL,s);
  459.         if (tstream) mail_find (tstream,s);
  460.           }
  461.         }
  462.         else if ((!strcmp (cmd,"BBOARDS"))||(!strcmp (cmd,"ALL.BBOARDS"))){
  463.           char tmp[MAILTMPLEN];
  464.           if (*s == '{') {    /* prepend leading * if remote */
  465.         sprintf (tmp,"*%s",s);
  466.         tstream = mail_open (NIL,tmp,OP_HALFOPEN);
  467.           }
  468.           if (*cmd == 'A') { /* want them all? */
  469.         mail_find_all_bboard (NIL,s);
  470.         if (tstream) mail_find_all_bboard (tstream,s);
  471.           }
  472.           else {        /* just subscribed */
  473.         mail_find_bboards (NIL,s);
  474.         if (tstream) mail_find_bboards (tstream,s);
  475.           }
  476.         }
  477.         else response = badfnd;
  478.         if (tstream) mail_close (tstream);
  479.         tstream = NIL;    /* no more temporary stream */
  480.       }
  481.     }
  482.  
  483.                 /* subscribe to mailbox or bboard */
  484.     else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
  485.       response = "%s OK SUBSCRIBE %s completed\015\012";
  486.                 /* get subcommand and true argument */
  487.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  488.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  489.         response = misarg;    /* missing required argument */
  490.       else if (arg) response = badarg;
  491.       else {        /* dispatch based on type */
  492.         if (!strcmp (cmd,"MAILBOX")) mail_subscribe (NIL,s);
  493.         else if (!strcmp (cmd,"BBOARD")) mail_subscribe_bboard (NIL,s);
  494.         else response = "%s BAD SUBSCRIBE option unrecognized: %s\015\012";
  495.       }
  496.     }
  497.                 /* unsubscribe to mailbox or bboard */
  498.     else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
  499.       response = "%s OK UNSUBSCRIBE %s completed\015\012";
  500.                 /* get subcommand and true argument */
  501.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  502.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  503.         response = misarg;    /* missing required argument */
  504.       else if (arg) response = badarg;
  505.       else {        /* dispatch based on type */
  506.         if (!strcmp (cmd,"MAILBOX")) mail_unsubscribe (NIL,s);
  507.         else if (!strcmp (cmd,"BBOARD")) mail_unsubscribe_bboard (NIL,s);
  508.         else response="%s BAD UNSUBSCRIBE option unrecognized: %s\015\012";
  509.       }
  510.     }
  511.                 /* create mailbox */
  512.     else if (!(anonymous || strcmp (cmd,"CREATE"))) {
  513.       if (!(s = snarf (&arg))) response = misarg;
  514.       else if (arg) response = badarg;
  515.       else if (state == OPEN) mail_create (stream,s);
  516.       else {        /* use the same driver as INBOX */
  517.         if (tstream = mail_open (NIL,"INBOX",OP_PROTOTYPE)) {
  518.           mail_create (tstream,s);
  519.           tstream = NIL;    /* drop prototype */
  520.         }
  521.         else response = "%s NO Can't %s without an INBOX";
  522.       }
  523.     }
  524.                 /* delete mailbox */
  525.     else if (!(anonymous || strcmp (cmd,"DELETE"))) {
  526.       if (!(s = snarf (&arg))) response = misarg;
  527.       else if (arg) response = badarg;
  528.       else mail_delete (NIL,s);
  529.     }
  530.                 /* rename mailbox */
  531.     else if (!(anonymous || strcmp (cmd,"RENAME"))) {
  532.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  533.       else if (arg) response = badarg;
  534.       else mail_rename (NIL,s,t);
  535.     }
  536.  
  537.                 /* purge cache */
  538.     else if (!strcmp (cmd,"PURGE")) {
  539.       response = "%s OK PURGE %s is unnecessary with this server\015\012";
  540.                 /* get subcommand */
  541.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s))))
  542.         response = misarg;    /* missing subcommand */
  543.       arg = strtok (NIL,"\015\012");
  544.       if (strcmp (cmd,"ALWAYS")) {
  545.         if (!(arg && (s = snarf (&arg)))) response = misarg;
  546.         else if (arg) response = badarg;
  547.         if (strcmp (cmd,"STATUS") && strcmp (cmd,"STRUCTURE") &&
  548.         strcmp (cmd,"TEXTS"))
  549.           response = "%s NO PURGE %s is unknown to this server\015\012";
  550.       }
  551.       else if (arg) response = badarg;
  552.     }
  553.  
  554.     else response = "%s BAD Command unrecognized: %s\015\012";
  555.     break;
  556.       default:
  557.         response = "%s BAD Server in unknown state for %s command\015\012";
  558.     break;
  559.       }
  560.                 /* change in recent messages? */
  561.       if ((state == OPEN) && (recent != stream->recent))
  562.     printf ("* %d RECENT\015\012",(recent = stream->recent));
  563.                 /* get text for alternative win message now */
  564.       if (response == altwin) cmd = lsterr;
  565.                 /* output response */
  566.       printf (response,tag,cmd,lsterr);
  567.     }
  568.     fflush (stdout);        /* make sure output blatted */
  569.   } while (state != LOGOUT);    /* until logged out */
  570.   exit (0);            /* all done */
  571. }
  572.  
  573.  
  574. /* Clock interrupt
  575.  */
  576.  
  577. void clkint ()
  578. {
  579.   fputs ("* BYE Autologout; idle for too long\015\012",stdout);
  580.   fflush (stdout);        /* make sure output blatted */
  581.                 /* try to gracefully close the stream */
  582.   if (state == OPEN) mail_close (stream);
  583.   stream = NIL;
  584.   exit (0);            /* die die die */
  585. }
  586.  
  587.  
  588. /* Kiss Of Death interrupt
  589.  */
  590.  
  591. void kodint ()
  592. {
  593.   long t = time (0);
  594.   if (state == OPEN) {        /* must be open for this to work */
  595.     fputs ("* OK [READ-ONLY] Now READ-ONLY.  Mailbox lock surrendered\015\012",
  596.        stdout);
  597.     fflush (stdout);        /* make sure output blatted */
  598.     stream->readonly = T;    /* make the stream readonly */
  599.     mail_ping (stream);        /* cause it to stick! */
  600.   }
  601. }
  602.  
  603. /* Snarf an argument
  604.  * Accepts: pointer to argument text pointer
  605.  * Returns: argument
  606.  */
  607.  
  608. char *snarf (char **arg)
  609. {
  610.   long i;
  611.   char *c = *arg;
  612.   char *s = c + 1;
  613.   char *t = NIL;
  614.   if (!c) return NIL;        /* better be an argument */
  615.   switch (*c) {            /* see what the argument is */
  616.   case '\0':            /* catch bogons */
  617.   case ' ':
  618.     return NIL;
  619.   case '"':            /* quoted string */
  620.     if (!(strchr (s,'"') && (c = strtok (c,"\"")))) return NIL;
  621.     break;
  622.   case '{':            /* literal string */
  623.     if (isdigit (*s)) {        /* be sure about that */
  624.       i = strtol (s,&t,10);    /* get its length */
  625.                 /* validate end of literal */
  626.       if (*t++ != '}' || *t++) return NIL;
  627.       fputs (argrdy,stdout);    /* tell client ready for argument */
  628.       fflush (stdout);        /* dump output buffer */
  629.                 /* get a literal buffer */
  630.       c = litbuf = (char *) fs_get (i+1);
  631.                 /* start timeout */
  632.       timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  633.       setitimer (ITIMER_REAL,&timer,NIL);
  634.       while (i--) *c++ = getchar ();
  635.       *c++ = NIL;        /* make sure string tied off */
  636.       c = litbuf;        /* return value */
  637.                     /* get new command tail */
  638.       if (!fgets ((*arg = t),TMPLEN - (t - cmdbuf) - 1,stdin)) _exit (1);
  639.                 /* clear timeout */
  640.       timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  641.       setitimer (ITIMER_REAL,&timer,NIL);
  642.       if (!strchr (t,'\012')) {    /* have a newline there? */
  643.     response = toobig;    /* lost it seems */
  644.     return NIL;
  645.       }
  646.       break;
  647.     }
  648.                 /* otherwise fall through (third party IMAP) */
  649.   default:            /* atomic string */
  650.     c = strtok (c," \015\012");
  651.     break;
  652.   }
  653.                 /* remainder of arguments */
  654.   if ((*arg = strtok (t,"\015\012")) && **arg == ' ') ++*arg;
  655.   return c;
  656. }
  657.  
  658. /* Fetch message data
  659.  * Accepts: sequence as a string
  660.  *        string of data items to be fetched
  661.  */
  662.  
  663. #define MAXFETCH 100
  664.  
  665. void fetch (char *s,char *t)
  666. {
  667.   char c,*v;
  668.   long i,k;
  669.   BODY *b;
  670.   int parse_bodies = NIL;
  671.   void (*f[MAXFETCH]) ();
  672.   char *fa[MAXFETCH];
  673.   if (!mail_sequence (stream,s)) {
  674.     response = badseq;        /* punt if sequence bogus */
  675.     return;
  676.   }
  677.                 /* process macros */
  678.   if (!strcmp (ucase (t),"ALL"))
  679.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  680.   else if (!strcmp (t,"FULL"))
  681.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  682.   else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  683.   s = t + strlen (t) - 1;    /* last character in attribute string */
  684.                 /* if multiple items, make sure in list form */
  685.   if (strchr (t,' ') && ((*t != '(') || (*s != ')'))) {
  686.     response = badatt;
  687.     return;
  688.   }
  689.                 /* nuke the parens now */
  690.   if ((*t == '(') && (*s == ')') && t++) *s = '\0';
  691.   k = 0;            /* initial index */
  692.   if (s = strtok (t," ")) do {    /* parse attribute list */
  693.     if (*s == 'B' && s[1] == 'O' && s[2] == 'D' && s[3] == 'Y') {
  694.       parse_bodies = T;        /* we will need to parse bodies */
  695.       switch (s[4]) {
  696.       case '\0':        /* entire body */
  697.     f[k++] = fetch_body;
  698.     break;
  699.       case '[':            /* body segment */
  700.     if ((v = strchr (s + 5,']')) && (!(*v = v[1]))) {
  701.       fa[k] = s + 5;    /* set argument */
  702.       f[k++] = fetch_body_part;
  703.       break;        /* valid body segment */
  704.     }            /* else fall into default case */
  705.       default:            /* bogus */
  706.     response = badatt;
  707.     return;
  708.       }
  709.     }
  710.     else if (!strcmp (s,"ENVELOPE")) f[k++] = fetch_envelope;
  711.     else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  712.     else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  713.     else if (!strcmp (s,"RFC822")) f[k++] = fetch_rfc822;
  714.     else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  715.     else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  716.     else if (!strcmp (s,"RFC822.TEXT")) f[k++] = fetch_rfc822_text;
  717.     else {            /* unknown attribute */
  718.       response = badatt;
  719.       return;
  720.     }
  721.   } while ((s = strtok (NIL," ")) && k < MAXFETCH);
  722.   else {
  723.     response = misarg;        /* missing attribute list */
  724.     return;
  725.   }
  726.   if (s) {            /* too many attributes? */
  727.     response = "%s BAD Excessively complex FETCH attribute list\015\012";
  728.     return;
  729.   }
  730.   f[k++] = NIL;            /* tie off attribute list */
  731.                 /* for each requested message */
  732.   for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->sequence) {
  733.                 /* parse envelope, set body, do warnings */
  734.     mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
  735.     printf ("* %d FETCH (",i);    /* leader */
  736.     (*f[0]) (i,fa[0]);        /* do first attribute */
  737.     for (k = 1; f[k]; k++) {    /* for each subsequent attribute */
  738.       putchar (' ');        /* delimit with space */
  739.       (*f[k]) (i,fa[k]);    /* do that attribute */
  740.     }
  741.     fputs (")\015\012",stdout);    /* trailer */
  742.   }
  743. }
  744.  
  745. /* Fetch message body structure
  746.  * Accepts: message number
  747.  *        string argument
  748.  */
  749.  
  750.  
  751. void fetch_body (long i,char *s)
  752. {
  753.   BODY *body;
  754.   mail_fetchstructure (stream,i,&body);
  755.   fputs ("BODY ",stdout);    /* output attribute */
  756.   pbody (body);            /* output body */
  757. }
  758.  
  759.  
  760. /* Fetch message body part
  761.  * Accepts: message number
  762.  *        string argument
  763.  */
  764.  
  765. void fetch_body_part (long i,char *s)
  766. {
  767.   unsigned long j;
  768.   long k = 0;
  769.   BODY *body;
  770.   int f = mail_elt (stream,i)->seen;
  771.   mail_fetchstructure (stream,i,&body);
  772.   printf ("BODY[%s] ",s);    /* output attribute */
  773.   if (body && (s = mail_fetchbody (stream,i,s,&j))) {
  774.                 /* and literal string */
  775.     printf ("{%d}\015\012",j);
  776.     while (j -= k) k = fwrite (s += k,1,j,stdout);
  777.     changed_flags (i,f);    /* output changed flags */
  778.   }
  779.   else fputs ("NIL",stdout);    /* can't output anything at all */
  780. }
  781.  
  782. /* Fetch IMAP envelope
  783.  * Accepts: message number
  784.  *        string argument
  785.  */
  786.  
  787. void fetch_envelope (long i,char *s)
  788. {
  789.   ENVELOPE *env = mail_fetchstructure (stream,i,NIL);
  790.   fputs ("ENVELOPE ",stdout);    /* output attribute */
  791.   penv (env);            /* output envelope */
  792. }
  793.  
  794. /* Fetch flags
  795.  * Accepts: message number
  796.  *        string argument
  797.  */
  798.  
  799. void fetch_flags (long i,char *s)
  800. {
  801.   char tmp[MAILTMPLEN];
  802.   unsigned long u;
  803.   char *t;
  804.   MESSAGECACHE *elt = mail_elt (stream,i);
  805.   s = tmp;
  806.   s[0] = s[1] = '\0';        /* start with empty flag string */
  807.                 /* output system flags */
  808.   if (elt->recent) strcat (s," \\Recent");
  809.   if (elt->seen) strcat (s," \\Seen");
  810.   if (elt->deleted) strcat (s," \\Deleted");
  811.   if (elt->flagged) strcat (s," \\Flagged");
  812.   if (elt->answered) strcat (s," \\Answered");
  813.   if (u = elt->user_flags) do    /* any user flags? */
  814.     if ((TMPLEN - ((s += strlen (s)) - tmp)) >
  815.     (2 + strlen (t = stream->user_flags[find_rightmost_bit (&u)]))) {
  816.       *s++ = ' ';        /* space delimiter */
  817.       strcpy (s,t);        /* copy the user flag */
  818.     }
  819.   while (u);            /* until no more user flags */
  820.   printf ("FLAGS (%s)",tmp+1);    /* output results, skip first char of list */
  821. }
  822.  
  823.  
  824. /* Output flags if was unseen
  825.  * Accepts: message number
  826.  *        prior value of Seen flag
  827.  */
  828.  
  829. void changed_flags (long i,int f)
  830. {
  831.   if (!f) {            /* was unseen? */
  832.     putchar (' ');        /* yes, delimit with space */
  833.     fetch_flags (i,NIL);    /* output flags */
  834.   }
  835. }
  836.  
  837.  
  838. /* Fetch message internal date
  839.  * Accepts: message number
  840.  *        string argument
  841.  */
  842.  
  843. void fetch_internaldate (long i,char *s)
  844. {
  845.   char tmp[MAILTMPLEN];
  846.   printf ("INTERNALDATE \"%s\"",mail_date (tmp,mail_elt (stream,i)));
  847. }
  848.  
  849. /* Fetch complete RFC-822 format message
  850.  * Accepts: message number
  851.  *        string argument
  852.  */
  853.  
  854. void fetch_rfc822 (long i,char *s)
  855. {
  856.   int f = mail_elt (stream,i)->seen;
  857.   printf ("RFC822 {%d}\015\012%s",mail_elt (stream,i)->rfc822_size,
  858.       mail_fetchheader (stream,i));
  859.   fputs (mail_fetchtext (stream,i),stdout);
  860.   changed_flags (i,f);        /* output changed flags */
  861. }
  862.  
  863.  
  864. /* Fetch RFC-822 header
  865.  * Accepts: message number
  866.  *        string argument
  867.  */
  868.  
  869. void fetch_rfc822_header (long i,char *s)
  870. {
  871.   fputs ("RFC822.HEADER ",stdout);
  872.   pstring (mail_fetchheader (stream,i));
  873. }
  874.  
  875.  
  876. /* Fetch RFC-822 message length
  877.  * Accepts: message number
  878.  *        string argument
  879.  */
  880.  
  881. void fetch_rfc822_size (long i,char *s)
  882. {
  883.   printf ("RFC822.SIZE %d",mail_elt (stream,i)->rfc822_size);
  884. }
  885.  
  886.  
  887. /* Fetch RFC-822 text only
  888.  * Accepts: message number
  889.  *        string argument
  890.  */
  891.  
  892. void fetch_rfc822_text (long i,char *s)
  893. {
  894.   int f = mail_elt (stream,i)->seen;
  895.   fputs ("RFC822.TEXT ",stdout);
  896.   pstring (mail_fetchtext (stream,i));
  897.   changed_flags (i,f);        /* output changed flags */
  898. }
  899.  
  900. /* Print envelope
  901.  * Accepts: body
  902.  */
  903.  
  904. void penv (ENVELOPE *env)
  905. {
  906.   if (env) {            /* only if there is an envelope */
  907.                 /* disgusting MacMS kludge */
  908.     if (mackludge) printf ("%d ",cstring (env->date) + cstring (env->subject) +
  909.                caddr (env->from) + caddr (env->sender) +
  910.                caddr (env->reply_to) + caddr (env->to) +
  911.                caddr (env->cc) + caddr (env->bcc) +
  912.                cstring (env->in_reply_to) +
  913.                cstring (env->message_id));
  914.     putchar ('(');        /* delimiter */
  915.     pstring (env->date);    /* output envelope fields */
  916.     putchar (' ');
  917.     pstring (env->subject);
  918.     putchar (' ');
  919.     paddr (env->from);
  920.     putchar (' ');
  921.     paddr (env->sender);
  922.     putchar (' ');
  923.     paddr (env->reply_to);
  924.     putchar (' ');
  925.     paddr (env->to);
  926.     putchar (' ');
  927.     paddr (env->cc);
  928.     putchar (' ');
  929.     paddr (env->bcc);
  930.     putchar (' ');
  931.     pstring (env->in_reply_to);
  932.     putchar (' ');
  933.     pstring (env->message_id);
  934.     putchar (')');        /* end of envelope */
  935.   }
  936.   else fputs ("NIL",stdout);    /* no envelope */
  937. }
  938.  
  939. /* Print body
  940.  * Accepts: body
  941.  */
  942.  
  943. void pbody (BODY *body)
  944. {
  945.   if (body) {            /* only if there is a body */
  946.     PARAMETER *param;
  947.     PART *part;
  948.     putchar ('(');        /* delimiter */
  949.                 /* multipart type? */
  950.     if (body->type == TYPEMULTIPART) {
  951.       for (part = body->contents.part; part; part = part->next)
  952.     pbody (&(part->body));    /* print each part */
  953.       putchar (' ');        /* space delimiter */
  954.       pstring (body->subtype);    /* and finally the subtype */
  955.     }
  956.     else {            /* non-multipart body type */
  957.       pstring ((char *) body_types[body->type]);
  958.       putchar (' ');
  959.       pstring (body->subtype);
  960.       if (param = body->parameter) {
  961.     fputs (" (",stdout);
  962.     do {
  963.       pstring (param->attribute);
  964.       putchar (' ');
  965.       pstring (param->value);
  966.       if (param = param->next) putchar (' ');
  967.     } while (param);
  968.     fputs (") ",stdout);
  969.       }
  970.       else fputs (" NIL ",stdout);
  971.       pstring (body->id);
  972.       putchar (' ');
  973.       pstring (body->description);
  974.       putchar (' ');
  975.       pstring ((char *) body_encodings[body->encoding]);
  976.       printf (" %d",body->size.bytes);
  977.       switch (body->type) {    /* extra stuff depends upon body type */
  978.       case TYPEMESSAGE:
  979.                 /* can't do this if not RFC822 */
  980.     if (strcmp (body->subtype,"RFC822")) break;
  981.     putchar (' ');
  982.     penv (body->contents.msg.env);
  983.     putchar (' ');
  984.     pbody (body->contents.msg.body);
  985.       case TYPETEXT:
  986.     printf (" %d",body->size.lines);
  987.     break;
  988.       default:
  989.     break;
  990.       }
  991.     }
  992.     putchar (')');        /* end of body */
  993.   }
  994.   else fputs ("NIL",stdout);    /* no body */
  995. }
  996.  
  997. /* Print string
  998.  * Accepts: string
  999.  */
  1000.  
  1001. void pstring (char *s)
  1002. {
  1003.   char c,*t;
  1004.   if (s) {            /* is there a string? */
  1005.                 /* must use literal string */
  1006.     if (strpbrk (s,"\012\015\"%{\\")) printf ("{%d}\015\012%s",strlen (s),s);
  1007.     else printf ("\"%s\"",s);    /* may use quoted string */
  1008.   }
  1009.   else fputs ("NIL",stdout);    /* empty string */
  1010. }
  1011.  
  1012.  
  1013. /* Print address list
  1014.  * Accepts: address list
  1015.  */
  1016.  
  1017. void paddr (ADDRESS *a)
  1018. {
  1019.   if (a) {            /* have anything in address? */
  1020.     putchar ('(');        /* open the address list */
  1021.     do {            /* for each address */
  1022.       putchar ('(');        /* open the address */
  1023.       pstring (a->personal);    /* personal name */
  1024.       putchar (' ');
  1025.       pstring (a->adl);        /* at-domain-list */
  1026.       putchar (' ');
  1027.       pstring (a->mailbox);    /* mailbox */
  1028.       putchar (' ');
  1029.       pstring (a->host);    /* domain name of mailbox's host */
  1030.       putchar (')');        /* terminate address */
  1031.     } while (a = a->next);    /* until end of address */
  1032.     putchar (')');        /* close address list */
  1033.   }
  1034.   else fputs ("NIL",stdout);    /* empty address */
  1035. }
  1036.  
  1037. /* Count string and space afterwards
  1038.  * Accepts: string
  1039.  * Returns: 1 plus length of string
  1040.  */
  1041.  
  1042. long cstring (char *s)
  1043. {
  1044.   char tmp[20];
  1045.   long i = s ? strlen (s) : 0;
  1046.   if (s) {            /* is there a string? */
  1047.                 /* must use literal string */
  1048.     if (strpbrk (s,"\012\015\"%{\\")) {
  1049.       sprintf (tmp,"{%d}\015\012",i);
  1050.       i += strlen (tmp);
  1051.     }
  1052.     else i += 2;        /* quoted string */
  1053.   }
  1054.   else i += 3;            /* NIL */
  1055.   return i + 1;            /* return string plus trailing space */
  1056. }
  1057.  
  1058.  
  1059. /* Count address list and space afterwards
  1060.  * Accepts: address list
  1061.  */
  1062.  
  1063. long caddr (ADDRESS *a)
  1064. {
  1065.   long i = 3;            /* open, close, and space */
  1066.                 /* count strings in address list */
  1067.   if (a) do i += 1 + cstring (a->personal) + cstring (a->adl) +
  1068.     cstring (a->mailbox) + cstring (a->host);
  1069.   while (a = a->next);        /* until end of address */
  1070.   else i = 4;            /* NIL plus space */
  1071.   return i;            /* return the count */
  1072. }
  1073.  
  1074. /* Co-routines from MAIL library */
  1075.  
  1076.  
  1077. /* Message matches a search
  1078.  * Accepts: IMAP2 stream
  1079.  *        message number
  1080.  */
  1081.  
  1082. void mm_searched (MAILSTREAM *s,long msgno)
  1083. {
  1084.                 /* nothing to do here */
  1085. }
  1086.  
  1087.  
  1088. /* Message exists (i.e. there are that many messages in the mailbox)
  1089.  * Accepts: IMAP2 stream
  1090.  *        message number
  1091.  */
  1092.  
  1093. void mm_exists (MAILSTREAM *s,long number)
  1094. {
  1095.   if (s != tstream) {        /* note change in number of messages */
  1096.     printf ("* %d EXISTS\015\012",(nmsgs = number));
  1097.     recent = -1;        /* make sure fetch new recent count */
  1098.   }
  1099. }
  1100.  
  1101.  
  1102. /* Message expunged
  1103.  * Accepts: IMAP2 stream
  1104.  *        message number
  1105.  */
  1106.  
  1107. void mm_expunged (MAILSTREAM *s,long number)
  1108. {
  1109.   if (s != tstream) printf ("* %d EXPUNGE\015\012",number);
  1110. }
  1111.  
  1112.  
  1113. /* Mailbox found
  1114.  * Accepts: Mailbox name
  1115.  */
  1116.  
  1117. void mm_mailbox (char *string)
  1118. {
  1119.   printf ("* MAILBOX %s\015\012",string);
  1120. }
  1121.  
  1122.  
  1123. /* BBoard found
  1124.  * Accepts: BBoard name
  1125.  */
  1126.  
  1127. void mm_bboard (char *string)
  1128. {
  1129.   printf ("* BBOARD %s\015\012",string);
  1130. }
  1131.  
  1132. /* Notification event
  1133.  * Accepts: IMAP2 stream
  1134.  *        string to log
  1135.  *        error flag
  1136.  */
  1137.  
  1138. void mm_notify (MAILSTREAM *s,char *string,long errflg)
  1139. {
  1140.   if (!tstream || (s != tstream)) switch (errflg) {
  1141.   case NIL:            /* information message, set as OK response */
  1142.   case PARSE:            /* parse glitch, output unsolicited OK */
  1143.     printf ("* OK %s\015\012",string);
  1144.     break;
  1145.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1146.     printf ("* NO %s\015\012",string);
  1147.     break;
  1148.   case ERROR:            /* error that broke command */
  1149.   default:            /* default should never happen */
  1150.     printf ("* BAD %s\015\012",string);
  1151.     break;
  1152.   }
  1153. }
  1154.  
  1155. /* Log an event for the user to see
  1156.  * Accepts: string to log
  1157.  *        error flag
  1158.  */
  1159.  
  1160. void mm_log (char *string,long errflg)
  1161. {
  1162.   switch (errflg) {        /* action depends upon the error flag */
  1163.   case NIL:            /* information message, set as OK response */
  1164.     if (response == win) {    /* only if no other response yet */
  1165.       response = altwin;    /* switch to alternative win message */
  1166.       fs_give ((void **) &lsterr);
  1167.       lsterr = cpystr (string);    /* copy string for later use */
  1168.     }
  1169.     break;
  1170.   case PARSE:            /* parse glitch, output unsolicited OK */
  1171.     printf ("* OK [PARSE] %s\015\012",string);
  1172.     break;
  1173.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1174.     if (strcmp (string,"Mailbox is empty")) printf ("* NO %s\015\012",string);
  1175.     break;
  1176.   case ERROR:            /* error that broke command */
  1177.   default:            /* default should never happen */
  1178.     response = lose;        /* set fatality */
  1179.     fs_give ((void **) &lsterr);/* flush old error */
  1180.     lsterr = cpystr (string);    /* note last error */
  1181.     break;
  1182.   }
  1183. }
  1184.  
  1185.  
  1186. /* Log an event to debugging telemetry
  1187.  * Accepts: string to log
  1188.  */
  1189.  
  1190. void mm_dlog (char *string)
  1191. {
  1192.   mm_log (string,WARN);        /* shouldn't happen normally */
  1193. }
  1194.  
  1195. /* Get user name and password for this host
  1196.  * Accepts: host name
  1197.  *        where to return user name
  1198.  *        where to return password
  1199.  *        trial count
  1200.  */
  1201.  
  1202. void mm_login (char *host,char *username,char *password,long trial)
  1203. {
  1204.   strcpy (username,user);    /* set user name */
  1205.   strcpy (password,pass);    /* and password */
  1206. }
  1207.  
  1208. /* About to enter critical code
  1209.  * Accepts: stream
  1210.  */
  1211.  
  1212. void mm_critical (MAILSTREAM *s)
  1213. {
  1214.   /* Not doing anything here for now */
  1215. }
  1216.  
  1217.  
  1218. /* About to exit critical code
  1219.  * Accepts: stream
  1220.  */
  1221.  
  1222. void mm_nocritical (MAILSTREAM *s)
  1223. {
  1224.   /* Not doing anything here for now */
  1225. }
  1226.  
  1227.  
  1228. /* Disk error found
  1229.  * Accepts: stream
  1230.  *        system error code
  1231.  *        flag indicating that mailbox may be clobbered
  1232.  * Returns: abort flag
  1233.  */
  1234.  
  1235. long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  1236. {
  1237.   if (serious) {        /* try your damnest if clobberage likely */
  1238.     fputs ("* BAD Retrying to fix probable mailbox damage!\015\012",stdout);
  1239.     fflush (stdout);        /* dump output buffer */
  1240.                 /* make damn sure timeout disabled */
  1241.     timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  1242.     setitimer (ITIMER_REAL,&timer,NIL);
  1243.     sleep (60);            /* give it some time to clear up */
  1244.     return NIL;
  1245.   }
  1246.                 /* otherwise die before more damage is done */
  1247.   printf ("* BYE Aborting due to disk error %s\015\012",strerror (errcode));
  1248.   return T;
  1249. }
  1250.  
  1251.  
  1252. /* Log a fatal error event
  1253.  * Accepts: string to log
  1254.  */
  1255.  
  1256. void mm_fatal (char *string)
  1257. {
  1258.   mm_log (string,ERROR);    /* shouldn't happen normally */
  1259. }
  1260.